---------------------------------------------------------------
-- @Copyright：           zour
-- @Description：         agent
---------------------------------------------------------------
local skynet = require "skynet"
local socketdriver = require "skynet.socket"
local websocket = require "http.websocket"
local serpent = require "serpent"
local log = require "sky_log"

local websocket_msg = {}
local Agent = {}
local gate
local client_fd

--pb
local protoc = require "protoc"	 
local pb = require "pb" 
local node_id = tonumber(skynet.getenv("node_id"))
local pb_list = require "pb_list"

--游戏
local room_pb_list
if node_id > 10000 then 
	room_pb_list = require "room_pb_list"
end

--============================= local function =============================--
--打包
local function encode(msgName, msgBody, packId)
	local msgBuf = pb.encode(msgName, msgBody)
	if  #msgBuf == 0 then
		log.error("msgBuf=", msgBuf.."|  msgName="..serpent.block(msgName)
			.."|  msgBody="..serpent.block(msgBody))
		return
	end

	--http://cloudwu.github.io/lua53doc/manual.html#6.4.2
    local encodeMsg = string.pack("=H", packId) .. msgBuf
    if not encodeMsg then
        log.error('msg pack failed' .. msgBuf)
        return
    end

    return encodeMsg
end

--解包
local function decode(rawData)
    --log.info("rawData=", rawData)
    --string:byte(x):返回string第几位的ascii编码
    local pack_id = rawData:byte(1) + rawData:byte(2) * 256  

    --sting:sub(x): 从第几位截取字符串 (1.2位表示包头地址，第3位开始为数据)
    local msgData = rawData:sub(3)
	
    local data, error, pb_files
	--游戏服有room_pb_list
	if room_pb_list then 
		--100以下为房间信息
		if pack_id > 100 then 
			pb_files = pb_list
		else
			pb_files = room_pb_list
		end
	else
		pb_files = pb_list
	end

	if pb_files[pack_id] then 
		data, error = pb.decode(pb_files[pack_id] ..".MsgBody", msgData)
	else
		log.error("pb_files[pack_id]==nil! pack_id=", pack_id)
		return false
	end
    
	if not data then 
    	log.error("error=", error)
        return false
	end

	local msg_title, msg_data
	for k, v in pairs(data) do
        msg_title = k 
        msg_data = v
	end

    return pack_id, msg_title, msg_data 
end

--============================= websocket_msg =============================--
function websocket_msg.connect(fd)
	--log.info("-------------websocket_msg.connect-----------------")
	--log.info("agent websocket_msg connect id=", fd)

	skynet.send(gate, "lua", "query_tables")
end

function websocket_msg.handshake(fd, header, url)
	--log.info("-------------websocket_msg.handshake-----------------")
	local addr = websocket.addrinfo(fd)
	--log.info("agent websocket_msg handshake, fd=", fd)
	--log.info("agent websocket_msg handshake, header=", header)
	--log.info("agent websocket_msg handshake, url=", url)
	--log.info("agent websocket_msg handshake, addr=", addr)


	--websocket.write(fd, "agent skynet handshake websocket ok!")
end

local function handle_msg(agent, msg)
	local pack_id, msg_title, msg_data = decode(msg)
    --log.debug("packid=", pack_id)
    --log.debug("msg_title=", msg_title)
    --log.debug("msg_data=", msg_data)
	if not pack_id then 
		return
	end

	local send_agent 
	if node_id > 10000 then
    	if pack_id > 100 then 
			send_agent = Agent.table_agent
   	 	else
			send_agent = ".service_room_manager"
    	end
		skynet.send(send_agent, "lua", "handle_msg", agent, pack_id, msg_title, msg_data)
	else
		local handler = require (pb_list[pack_id])
		local send_msg
		if handler[msg_title] then 
			send_msg = handler[msg_title](handler, agent, msg_data)
		end
		if send_msg and type(send_msg)=='table' then 
			log.debug("send_msg=", send_msg)
			local send_msg_buf = encode(pb_list[pack_id]..".MsgBody", send_msg, pack_id)
			websocket.write(client_fd, send_msg_buf)
		end	
	end
 end

function websocket_msg.message(fd, msg, msg_type)
	--log.debug("-------------websocket_msg.message-----------------")
	--log.debug("websocket_msg message fd=", fd)
	--log.debug("websocket_msg message client_fd=", client_fd)
    --log.debug("websocket_msg message msg=", msg)
	--log.debug("websocket_msg message msg_type=", msg_type)
	--log.debug("self=", skynet.self())

    assert(msg_type == "binary" or msg_type == "text")

	handle_msg(skynet.self(), msg)
end

function websocket_msg.close(fd)
	log.debug("-------------websocket_msg.close-----------------")
	socketdriver.close(fd)

	skynet.send(gate, "lua", "disconnect", fd)
end

function websocket_msg.error(fd, msg)
    --log.info("websocket_msg error msg=", msg)
    socketdriver.shutdown(fd)

	skynet.send(gate, "lua", "error", fd, msg)
end

--============================= Agent =============================--
function Agent:start(conf)
	gate = conf.gate
	--watchdog = conf.watchdog
	client_fd = conf.client
	skynet.call(gate, "lua", "forward", client_fd)
end

function Agent:openclient(fd, addr)
	local ok, err = websocket.accept(fd, websocket_msg, "ws", addr)
	if not ok then
		log.info(err)
	end
end

function Agent:disconnect()
	log.info("disconnect: agent exit! player_id=", (self.player_id or  "nil").."  room_id="..(self.room_id or "nil").."  table_id="..(self.table_id or "nil"))
	-- todo: do something before exit
	if node_id > 10000 then 
		skynet.send(".service_room_manager", "lua", "disconnect", skynet.self(), self.player_id, self.room_id, self.table_id)
	end

	skynet.exit()
end

function Agent:send_msg(msg, pack_id)
	--log.debug("msg=", msg)
	--log.debug("pack_id=", pack_id)
	--100以下是游戏公共pb，游戏的从100开始
	--log.debug("node_id=", node_id)
	--log.debug("pack_id=", pack_id)
	
    local pb_files 
	if node_id < 10000 then 
		pb_files = pb_list
	else
		if pack_id > 100 then 
			pb_files = pb_list
		else
			pb_files = room_pb_list
		end
	end
	--log.debug("pb_files=", pb_files)
	
	if msg and type(msg)=='table' then 
		--log.debug("msg=", msg)
		--log.debug("pack_id=", pack_id)
		--log.debug("pb_files=", pb_files)
		local send_msg_buf = encode(pb_files[pack_id]..".MsgBody", msg, pack_id)
		websocket.write(client_fd, send_msg_buf)

		--log.debug("send_msg_buf pb_files[pack_id]=", pb_files[pack_id]..".MsgBody")
		--log.debug("send_msg_buf msg=", msg)
		--log.debug("send_msg_buf pack_id=", pack_id)
		--log.debug("send_msg_buf send_msg_buf=", send_msg_buf)
	end
end

function Agent:login_in(room_agent, info, pack_id)
	--log.debug("enter_room room_agent=", room_agent )
	log.debug("enter_room info=", info )
	--log.debug("enter_room pack_id=", pack_id )

	self.player_id = info.player_id
	self.room_id = info.room_id
	self.room_agent = room_agent
	self:send_msg(info, pack_id)
end

function Agent:enter_game(table_agent, info, pack_id)
	log.debug("Agent:enter_game table_id=", info.table_id)
	self.table_id = info.table_id
	self.table_agent = table_agent
	self:send_msg(info, pack_id)
end

function Agent:init()
	local dir = skynet.getenv("proto")
	log.debug("dir=", dir)
	log.debug("pb_list=", pb_list)
	for _, v in pairs(pb_list) do
		protoc:loadfile(dir..v..".proto")
	end

	if node_id > 10000 then 
		local room_dir = skynet.getenv("room_proto")
    	log.debug("room_proto=", room_dir)
		log.debug("room_pb_list=", room_pb_list)
		for _, v in pairs(room_pb_list) do
			protoc:loadfile(room_dir..v..".proto")
		end
	end
end

skynet.register_protocol {
	name = "client",
	id = skynet.PTYPE_CLIENT,
	unpack = function (msg, sz)
		return msg, sz
	end,
	dispatch = function (fd, address, msg, sz)
		assert(fd == client_fd)	-- You can use fd to reply message
		skynet.ignoreret()	-- session is fd, don't call skynet.ret
		--skynet.trace()
		log.info("agent register_protocol msg= ", msg)
		log.info("agent register_protocol sz= ", sz)

		--dispatch_message(skynet.unpack(msg, sz))
	end
}

skynet.start(function()
	skynet.dispatch("lua", function(session, source, command, ...)
		--skynet.trace()
		--log.info("session=", session)
		--log.info("source=", source)
		--log.info("command=", command)
		local f = Agent[command]
		if f then  
			skynet.ret(skynet.pack(f(Agent, ...)))
		else
			log.error(string.format("Unknown command %s", tostring(command)))
		end
	end)

	Agent:init()
end)
